#ifndef __RXMPE_TOKEN_PARSER__
#define __RXMPE_TOKEN_PARSER__
#include <string>
#include <vector>

namespace xm
{
	template < bool ret >
	inline bool message( char* fmt, ... )
	{
		//__asm int 3;
		va_list va;
		va_start( va, fmt );
		vprintf( fmt, va );
		return ret;
	}

	enum TypeToken
	{
		TTUnknown,
		TTCommand,
		TTControl,
		TTNumber,
		TTHexNumber,
		TTString,
		TTHexString,
		TTStringW,
		TTDigitPair,
		TTDataBlock,
		TTAdapter,
	};

	struct Token
	{
		TypeToken		type;
		size_t			rawpos;
		std::string		stuff;
		int				numLine;

		Token( TypeToken t, int r ) : numLine(r), type(t) {}

		bool operator == ( const Token & r ) const
		{
			if( type != r.type ) return false;
			return 0 == stricmp( stuff.c_str(), r.stuff.c_str() );
		}
	};

	static bool token_to_binary(Token & tk, void* buff, int len)
	{
		if(tk.type != TTString)
			return false;
	
		BYTE aByte = 0;
		std::string& rBinStr = tk.stuff;
		int nStrLen = rBinStr.size();
		int i = 0, n = 0;
		for(; i < nStrLen; i++)
		{
			CHAR chr = rBinStr[i];
			aByte <<= 1;
			if(chr == '1')				
				aByte |= 1;
			else if(chr == '0')
				aByte &= ~1;
			else
				return false;
			
			if(i%8 == 7)
			{
				n = i/8;
				if(n >= len)
					return false;

				((BYTE*)buff)[n] = aByte;
				aByte = 0;
			}
		}
	
		if(i%8 > 0)
		{
			n = i/8;
			if(n >= len)
				return false;

			((BYTE*)buff)[n] = aByte;
		}
		return true;
	}

	static bool token_to_pair(Token & tk, int& num1, int &num2)
	{
		if(tk.type != TTDigitPair)
			return false;

		if( sscanf(tk.stuff.c_str(), "%d,%d", &num1, &num2) != 2)
			return false;

		return true;
	}

	static bool token_to_number( Token & tk, int & num )
	{
		int tmp = 0;
		if( tk.type == TTNumber )
		{
			if( 1 != sscanf( tk.stuff.c_str(), "%d", &tmp ) )
				return false;
			num = tmp;
		}
		else if( tk.type == TTHexNumber )
		{
			// skip 0x
			if( 1 != sscanf( tk.stuff.c_str()+2, "%x", &tmp ) )
				return false;
			num = tmp;
		}
		else 
			return false;
		return true;
	}
	static bool token_to_number64( Token & tk, UINT64 & num )
	{
		UINT64 tmp = 0;
		if( tk.type == TTNumber )
		{
			if( 1 != sscanf( tk.stuff.c_str(), "%I64d", &tmp ) )
				return false;
			num = tmp;
		}
		else if( tk.type == TTHexNumber )
		{
			// skip 0x
			if( 1 != sscanf( tk.stuff.c_str()+2, "%I64x", &tmp ) )
				return false;
			num = tmp;
		}
		else 
			return false;
		return true;
	}

	#define ISHEXCHAR( a )		( ( tolower(a) >= 'a' ) && ( tolower(a) <= 'f' ) )

	struct TokenParser
	{
	public:
		TokenParser()
		{
			memset( szLogMessage, 0, sizeof(szLogMessage) );
		}
	protected:

		CHAR	szLogMessage[512];

		template < BOOL RET >
		inline BOOL LogMessage( char* fmt, ... )
		{
			va_list va;
			va_start( va, fmt );
			vsnprintf( szLogMessage, sizeof(szLogMessage)-1, fmt, va );
			return RET;
		}

		inline bool _is_space( char ch )
		{
			return ( !ch || isspace(ch) );
		}

		typedef std::vector< Token > TokenList;

		TokenList	_tk_list;

	public:

		LPCSTR handlePound( LPCSTR p, LPCSTR pe )
		{
			while( p < p && isalnum(*p) ) ++ p;
			return p;
		}

		SIZE_T count()
		{
			return _tk_list.size();
		}

		Token & item( SIZE_T index )
		{
			return _tk_list[index];
		}

		BOOL Parse( LPCSTR text, SIZE_T len = 0 )
		{
			if( !len ) len = strlen( text );
			LPCSTR p = text;
			LPCSTR end = text + len;

			int	numLine = 1;
			enum 
			{
				skip_space,
				scan_command,
				scan_comment,
				scan_line_comment_end,
				scan_block_comment_end,
				scan_digtal,
				scan_string,
				scan_string2,
				scan_wstring,
				scan_digtal_hex,
				scan_digtal_dec,
				scan_digtal_neg,
				scan_control,
				scan_control_end,
				scan_pair,
				scan_block_or,
				scan_adapter,
				scan_shortcmd,
			};

			int state = skip_space;
			LPCSTR px = 0;
			bool bUnicodeString = false;

			for( ; p < end; ++ p )
			{
				struct LineCounter
				{
					int&	_numLine;
					int		_newLine;	
					LineCounter( int & nl, bool bInc ) : _numLine(nl) 
					{
						if( bInc ) _newLine = _numLine + 1;
						else _newLine = _numLine;
					};
					~LineCounter()
					{
						_numLine = _newLine;
					}
				};

				LineCounter _line( numLine, *p == '\n' );

				if (*p == '\n')
					bUnicodeString = false;

				char ch = *p;
				if( state == skip_space )
				{
					if( _is_space(ch) )		continue;
					if( ch == ':' )			state = scan_command;
					else if( ch == '\"' )	state = scan_string;
					else if( ch == '0' )	state = scan_digtal;
					else if( isdigit(ch) )	state = scan_digtal_dec;
					else if( ch == '-' )		state = scan_digtal_neg;
					else if( ch == '.' )	state = scan_control;
					else if( ch == '[')		state = scan_control;
					//else if( ch == ']')		state = scan_control_end;
					else if( ch == '/' )	state = scan_comment;
					else if( ch == '#' )	state = scan_line_comment_end;
					else if( ch == '\'' )	state = scan_string2;
					else if( ch == 'L' )	state = scan_wstring;
					else if( ch == '(')		state = scan_pair;
					else if( ch == '{')		state = scan_block_or;
					else if( ch == '=')		state = scan_adapter;
					else if( ch == '>')		state = scan_shortcmd;
					else if( ch == '<')		state = scan_shortcmd;
					else if( ch == ']' )
					{
						Token tk( TTControl, numLine );
						tk.rawpos = p - text;
						tk.stuff.assign( p, p+1 );
						_tk_list.push_back( tk );
						state = skip_space;
					}
					else 
					{
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character: %02X .", numLine, ch );
					}
					px = p;
				}
				else if( state == scan_shortcmd )
				{
					if( p - px > 3 ) 
					{
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character: %02X .", numLine, ch );
					}

					if( ch == *px ) continue;

					if( p - px < 2 ) 
					{
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character: %02X .", numLine, ch );
					}

					Token tk( TTControl, numLine );
					tk.rawpos = px - text;
					tk.stuff.assign( px, p );
					_tk_list.push_back( tk );
					state = skip_space;

					-- p;
				}
				else if( state == scan_comment )
				{
					if( ch == '*' ) 
						state = scan_block_comment_end;
					else if( ch == '/' )
						state = scan_line_comment_end;
					else 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character: %02X .", numLine, ch );
				}
				else if( state == scan_block_comment_end )
				{
					if( p[0] == '/' && p[-1] == '*' )
						state = skip_space;
					//return LogMessage<FALSE>( "[token] (line: %d) block-comment is not closed.", numLine );
				}
				else if( state == scan_line_comment_end )
				{
					if( p[0] == '\r' || p[0] == '\n' ) 
						state = skip_space;
				}
				else if( state == scan_control )
				{
					if( isalnum(ch) ) continue;
					if( strchr( "=-:", ch ) ) continue;
					if( _is_space(ch) ) 
					{
						Token tk( TTControl, numLine );
						tk.rawpos = px - text;
						tk.stuff.assign( px, p );
						_tk_list.push_back( tk );
						state = skip_space;
					}
					else
					{
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Control-Command}.", numLine, ch );
					}
				}
				else if( state == scan_digtal_neg )
				{
					if( isdigit(ch) ) 
						state = scan_digtal;
					else
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Digit}.", numLine, ch );
				}
				else if( state == scan_command )
				{
					if( isalnum(ch) ) continue;
					if( _is_space(ch) ) 
					{
						Token tk( TTCommand, numLine );
						tk.rawpos = px - text;
						tk.stuff.assign( px, p );
						_tk_list.push_back( tk );
						state = skip_space;
					}
					else 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Command}.", numLine, ch );
				}
				else if( state == scan_adapter )
				{
					if( isalnum(ch) ) continue;
					if( _is_space(ch) ) 
					{
						Token tk( TTAdapter, numLine );
						tk.rawpos = px - text;
						tk.stuff.assign( px, p );
						_tk_list.push_back( tk );
						state = skip_space;
					}
					else 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Command}.", numLine, ch );
				}
				else if( state == scan_digtal )
				{
					if( ch == 'x' || ch == 'X' ) 
						state = scan_digtal_hex;
					else if( isdigit(ch) ) 
					{
						state = scan_digtal_dec;
					}
					else if( _is_space(ch) || ch == ']' )
					{
						Token tk( TTNumber, numLine );
						tk.type = TTNumber;
						tk.rawpos = px - text;
						tk.stuff.assign( px, p );
						_tk_list.push_back( tk );
						state = skip_space;
						if( ch == ']' ) -- p;
					}
					else
					{
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Digit//DEC}.", numLine, ch );
					}
				}
				else if( state == scan_digtal_dec )
				{
					if( isdigit(ch) ) 
						continue;

					if( ch != ']' && !_is_space(ch) ) 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Digit//DEC}.", numLine, ch );

					Token tk( TTNumber, numLine );
					tk.rawpos = px - text;
					tk.stuff.assign( px, p );
					_tk_list.push_back( tk );
					state = skip_space;
					
					if( ch == ']' ) -- p;

				}
				else if( state == scan_digtal_hex )
				{
					if( isdigit(ch) ) 
						continue;

					if( ISHEXCHAR(ch) ) 
						continue;

					if( ch != ']' && !_is_space(ch) ) 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {Digit//HEX}.", numLine, ch );

					Token tk( TTHexNumber, numLine );
					tk.rawpos = px - text;
					tk.stuff.assign( px, p );
					_tk_list.push_back( tk );

					if( ch == ']' ) -- p;
					state = skip_space;
				}
				else if( state == scan_wstring )
				{
					if( ch != '\"' ) 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {UnicodeString}.", numLine, ch );
					bUnicodeString = true;
					state = scan_string;
				}
				else if( state == scan_string )
				{
					if( ch == '\\' ) p ++;	// skip one
					if( ch != '\"' ) 
						continue;

					Token tk( TTString, numLine );
					tk.rawpos = px - text;
					
					BOOL isUni = bUnicodeString;
					bUnicodeString = FALSE;

					if( isUni ) tk.type = TTStringW;
					tk.stuff.assign( px+(isUni?2:1), p );				
					_tk_list.push_back( tk );
					state = skip_space;
				}
				else if( state == scan_string2 )
				{
					if( isdigit(ch) || ISHEXCHAR(ch) )
						continue;

					if( ch != '\'' ) 
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {HEX String}.", numLine, ch );

					Token tk( TTHexString, numLine );
					tk.rawpos = px - text;
					tk.stuff.assign( px+1, p );

					if( tk.stuff.size() % 2 ) 
						return LogMessage<FALSE>( "[token] (line: %d) invalid HEX String: %s.", numLine, tk.stuff.c_str() );

					_tk_list.push_back( tk );

					state = skip_space;
				}
				else if( state == scan_block_or )
				{
					if( end - p < 5 || memcmp( p, "{{", 2 ) )
						return false;
					const char * pbs = p + 2;
					const char * pbe = strstr( pbs, "}}}" );
					if( !pbe ) return false;
					Token tk( TTDataBlock, numLine );
					tk.rawpos = pbs - text;
					tk.stuff.assign( pbs, pbe );
					_tk_list.push_back( tk );
					p = pbe+2;
					state = skip_space;
				}
				else if( state == scan_pair)
				{
					if( isdigit(ch) || isspace(ch) || ch == ',')
					{
						continue;
					}
					else if( ch == '(' )
					{
						return LogMessage<FALSE>( "[token] (line: %d) unclose digital pair when parsing {DigitPair}.", numLine);
					}
					else if( ch == ')' )
					{
						Token tk( TTDigitPair, numLine );
						tk.rawpos = px - text;
						tk.stuff.assign( px + 1, p );

						_tk_list.push_back( tk );
						state = skip_space;						
					}
					else
					{
						return LogMessage<FALSE>( "[token] (line: %d) unexpected character %02X when parsing {DigitPair}.", numLine, ch);
					}				
				}
			}

			if( state != skip_space ) 
				return LogMessage<FALSE>( "[token] (line: %d) unexpected file end.", numLine );

			return true;
		}
	};
};

#endif